<?php

namespace common\components;


Class HttpClient
{
    const APPLICATION_TYPE_XML = 'xml';
    const APPLICATION_TYPE_JSON = 'json';
    const APPLICATION_TYPE_TEXT = 'text';

    public $_error;

    /**
     * 请求头
     * @var array
     */
    protected $_headers = [];

    /**
     * curl请求句柄
     * @var resource
     */
    protected $ch;

    /**
     * 存放响应头
     * @var
     */
    protected $responseHeader;

    /**
     * 请求类型
     * @var bool
     */
    public $application_type = self::APPLICATION_TYPE_TEXT;
    public $response_type = self::APPLICATION_TYPE_JSON;

    /**
     * 初始化请求uri
     * 检查扩展curl
     * RequestApi constructor.
     * @throws \Exception
     */
    public function __construct()
    {
        if ( !extension_loaded('curl') )
        {
            throw new \Exception('请安装curl扩展');
        }

        $this->ch = curl_init();
    }

    /**
     * 设置字符集
     * @param $charset
     */
    public function setCharset($charset)
    {
        $this->setHeaders("charset", $charset);
    }

    /**
     * 设置请求头
     * @param $key
     * @param $value
     */
    public function setHeaders($key, $value)
    {
        $this->_headers[$key] = $value;
        return ;
    }

    /**
     * 载入header头
     */
    protected function loadHeaders()
    {
        if ( empty($this->ch) )
        {
            $this->resetCurl();
        }

        $arrHeader = [];

        foreach ( $this->_headers as $key=>$value )
        {
            $arrHeader[] = $key.':'.$value;
        }

        curl_setopt($this->ch, CURLOPT_HTTPHEADER, $arrHeader);

        return ;
    }

    /**
     * 发送请求
     * @param $url
     * @param string $type
     * @param $data
     * @param bool $isSSL
     * @param int $timeout
     * @return bool|mixed
     */
    public function sendRequest($url, $type='POST', array $data = [], $isSSL=true, $timeout=3600)
    {
        switch ( strtolower($this->application_type) )
        {
            case self::APPLICATION_TYPE_JSON :
                $data = json_encode($data);
                break;
            case self::APPLICATION_TYPE_XML :
                $data = $this->arrayToXml($data);
                break;
            default :
                $data = http_build_query($data);
        }

        #加载请求头
        $this->loadHeaders();

        #设置请求地址
        curl_setopt($this->ch, CURLOPT_URL, $url);
        #curl_setopt($this->ch, CURLOPT_HEADER, true);

        curl_setopt($this->ch, CURLINFO_HEADER_OUT, TRUE);
        curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($this->ch, CURLOPT_CONNECTTIMEOUT, $timeout);

        switch ( strtoupper($type) )
        {
            case 'GET' :
                $url = self::createUrlParams($url, $data);#die(var_dump($url));
                curl_setopt($this->ch, CURLOPT_URL, $url);
                curl_setopt($this->ch, CURLOPT_HTTPGET, true);
                break;
            case 'POST' :
                curl_setopt($this->ch, CURLOPT_POST, true);
                curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
                break;
            case 'PUT' :
                curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, 'PUT');
                curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
                break;
            case 'DELETE' :
                curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
                curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
                break;
            default :
                return false;
        }

        if ( $isSSL ) {
            curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, false);
        }

        #获取返回结果
        $ch_ret = curl_exec($this->ch);
        $curlInfo = curl_getinfo($this->ch);
        $this->responseHeader = @$curlInfo["request_header"];
        $error = curl_errno($this->ch);

        if( $error ) {
            $this->_error = curl_error($this->ch);
        }

        switch ( $this->response_type ) {
            case self::APPLICATION_TYPE_JSON:
                $ch_ret = self::parseJson($ch_ret);
                break;
            case self::APPLICATION_TYPE_XML:
                $ch_ret = self::xmlToArray($ch_ret);
                break;
            default:
                break;
        }

        return $ch_ret;
    }

    /**
     * @param bool $isParse
     * @return array|string
     */
    public function getResponseHeader($isParse = true)
    {
        if ( !$isParse ) {
            return $this->responseHeader;
        }
        $headers = explode("\r\n", $this->responseHeader);
        $ret = [];
        foreach ($headers as $v){
            if(strpos($v,': ')){
                list($name, $val) = explode(': ', $v, 2);
                $ret[$name] = $val;
                //echo $name,':'.$val.PHP_EOL;
            }
        }
        return $ret;
    }
    public function getHttpCode()
    {
        return curl_getinfo($this->ch,CURLINFO_HTTP_CODE);
    }

    public function resetCurl()
    {
        $this->ch = curl_init();
        return ;
    }

    public function closeCurl()
    {
        curl_close($this->ch);
        return ;
    }

    public static function createUrlParams($url, $params)
    {
        if ( !$params ) {
            return $url;
        }

        $params = is_array($params) ? http_build_query($params) : $params;

        return strpos($url, '?') === false ? ($url . '?' . $params) : ($url . '&' . $params);
    }

    public static function createUrlPath($url, $path)
    {
        $url = rtrim($url, '/') . '/' . ltrim($path, '/');

        if ( func_num_args() > 2 ) {
            $args = array_slice(func_get_args(), 2);
            foreach ( $args as $arg ) {
                $url = rtrim($url, '/') . '/' . ltrim($arg);
            }
        }

        return $url;
    }

    /**
     * 数组转化xml
     * @param $array
     * @param bool $xml
     * @return mixed
     */
    public static function arrayToXml($array, $xml = false){
        if($xml === false){
            $xml = new \SimpleXMLElement('<xml/>');
        }
        foreach($array as $key => $value){
            if(is_array($value)){
                self::arrayToXml($value, $xml->addChild($key));
            }else{
                $xml->addChild($key, $value);
            }
        }
        return $xml->asXML();
    }

    /**
     * 将xml转换为数组
     * @param string $xml:xml文件或字符串
     * @return array
     */
    public static function xmlToArray($xml)
    {
        //考虑到xml文档中可能会包含<![CDATA[]]>标签，第三个参数设置为LIBXML_NOCDATA
        if (file_exists($xml)) {
            libxml_disable_entity_loader(false);
            $xml_string = simplexml_load_file($xml,'SimpleXMLElement', LIBXML_NOCDATA);
        }else{
            libxml_disable_entity_loader(true);
            $xml_string = simplexml_load_string($xml,'SimpleXMLElement', LIBXML_NOCDATA);
        }
        $result = json_decode(json_encode($xml_string),true);
        return $result;
    }


    public static function parseJson($jsonData)
    {
        if ( !$jsonData ) {
            return false;
        } elseif ( is_array($jsonData) ) {
            return $jsonData;
        }

        try {
            $jsonData = json_decode($jsonData, true);
        } catch ( \InvalidArgumentException $e ) {
            return false;
        }

        return $jsonData;
    }

}
